import { BaseAction } from "./actions/action";
import { AutherizeAction } from "./actions/autherizeAction";
import { ActivityStatus, ExecuteStatus, ExecutiveContext, State } from "./defination";
import { ExecutionRecord, ExecutiveRecord } from "./entity";
import { ExecutiveObject } from "./executive";
import { ACTIVITY, ACTIVITY_STATUS,EXECUTE_STATUS, CONTEXT, EXECUTION_RECORD } from "./shared-symbols";

const HEAD_ACTION = Symbol("headAction")
const TAIL_ACTION = Symbol("tailAction")
const FROM_STATUS = Symbol("fromStatus")
const TO_STATUS = Symbol("toStatus")


export class Execution<
TActivity extends ExecutiveObject<TActivity,TState,TRecord,TContext>,
TState extends State=State,
TRecord extends ExecutiveRecord=ExecutiveRecord,
TContext extends ExecutiveContext=ExecutiveContext
> {
  [HEAD_ACTION]: AutherizeAction<TActivity,TState,TRecord,TContext>
  [TAIL_ACTION]: BaseAction<TActivity,TState,TRecord,TContext>
  [FROM_STATUS]: ActivityStatus
  [TO_STATUS]?: ActivityStatus
  [EXECUTE_STATUS]: ExecuteStatus
  [ACTIVITY]: TActivity
  [CONTEXT]: TContext
  public constructor(executive: TActivity, context: TContext){
    this[ACTIVITY] = executive
    this[CONTEXT] = context
    this[TAIL_ACTION] = this[HEAD_ACTION] = new AutherizeAction(executive)
    this[TO_STATUS] = this[FROM_STATUS] = executive[ACTIVITY_STATUS]
    this[EXECUTE_STATUS] = executive[EXECUTE_STATUS]
  }

  public async run(): Promise<Execution<TActivity,TState,TRecord,TContext>>{
    let action:BaseAction<TActivity,TState,TRecord,TContext>|null|undefined = this[HEAD_ACTION]
    const activity = this[ACTIVITY]
    while(action){
      this[TAIL_ACTION] = action
      this[TO_STATUS] = activity[ACTIVITY_STATUS]
      this[EXECUTE_STATUS] = activity[EXECUTE_STATUS]
      action = await action.invoke(this[CONTEXT])
    }
    return this
  }

  get headAction(): AutherizeAction<TActivity,TState,TRecord,TContext>{
    return this[HEAD_ACTION]
  }
  get tailAction(): BaseAction<TActivity,TState,TRecord,TContext>{
    return this[TAIL_ACTION]
  } 
  get fromStatus(): ActivityStatus{
    return this[FROM_STATUS]
  }
  get toStatus(): ActivityStatus|undefined{
    return this[TO_STATUS]
  }
  get executeStatus(): ExecuteStatus{
    return this[EXECUTE_STATUS]
  }
  get activity(): TActivity{
    return this[ACTIVITY]
  } 
  get context(): TContext{
    return this[CONTEXT]
  }
  get record(): ExecutionRecord|undefined{
    return this[TAIL_ACTION][EXECUTION_RECORD]
  }

  /**
   * Implements the iterator protocol to allow iterating through the action chain
   * using for...of loops. Starts with headAction and follows the next() chain
   * until null or undefined is reached.
   */
  [Symbol.iterator](): Iterator<BaseAction<TActivity,TState,TRecord,TContext>> {
    let currentAction:BaseAction<TActivity,TState,TRecord,TContext>|null|undefined = this[HEAD_ACTION];
    
    return {
      next: () => {
        if (!currentAction) {
          return { done: true, value: undefined };
        }
        
        const value = currentAction;
        currentAction = currentAction.next;
        return { 
          done: false, 
          value 
        };
      }
    };
  }
}
